package com.apes.framework.plugin.form.parser.element;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.plugin.filereader.xml.ApesElement;
import com.apes.framework.plugin.filereader.xml.ApesElementParser;
import com.apes.framework.plugin.form.FormConst;
import com.apes.framework.plugin.form.parser.matcher.AttrsMatcher;
import com.apes.framework.plugin.form.parser.matcher.DomainMatcher;
import com.apes.framework.plugin.form.parser.matcher.ExpressionMatcher;
import com.apes.framework.plugin.form.parser.node.FormNodeParser;
import com.apes.framework.util.LogicalUtil;
import com.apes.framework.util.Operation;
import com.apes.framework.util.Tools;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.text.MessageFormat.format;

/**
 * 功能：field解析
 *
 * @author xul
 * @create 2018-04-16 16:12
 */
@Component("form.fieldElementParser")
public class FieldElementParser implements ApesElementParser<ApesElement, Map> {

    /**
     * 可解析的元素列表
     **/
    private final String[] elementNames = {"field"};

    /**
     * 功能：可解析节点域
     */
    @Override
    public String getDomain() {
        return FormNodeParser.domain;
    }

    /**
     * 功能：可解析的元素列表
     */
    @Override
    public String[] getNames() {
        return elementNames;
    }

    @Override
    public Map parse(ApesElement element) {
        Map node = new HashMap();
        if (!StringUtils.isEmpty(element.getText())){
            node.put("widgets",element.getText());
            JSONObject json = (JSONObject) JSON.parse(node.get("widgets").toString());
            node.putAll(json);
        }
        parseAttributes(element, node);
        parseNestedElements(element, node);
        return node;
    }

    private void parseAttributes(ApesElement element, Map node) {
        if (element.getParent().getName().equals("search")) {
            Map templateOptions = new HashMap();
            Map config = new HashMap();
            Map<String, String> lifecycle = new HashMap();
            node.put("type", "conditionInput");
            config.put("value", "");
            element.getAttributes().forEach((k, v) -> {
                if (k.trim().equals("name")) {
                    String value = v.replaceAll("\\.", "#");
                    node.put("key", value);
                } else if (k.equals("defaultValue")) {
                    node.put(k, v.trim());
                    templateOptions.put(k, v);
                } else if (k.trim().equals("label") || k.trim().equals("placeholder") || k.trim().equals("display")) {
                    templateOptions.put(k, v);
                } else if (k.equals("domain")) {
                    templateOptions.put(k.trim(), new DomainMatcher().parse(v.trim()));
                } else if (k.equals("widget")) {
                    config.put("type", v.trim());
                } else if (k.equals("operators")) {
                    String type = Operation.getSearchType(v.trim());
                    if (!Tools.isNull(type)) {
                        List operators = new ArrayList<String>();
                        operators.add(type);
                        config.put("operators", operators);
                    }
                } else if (k.equals("divCol") || k.equals("lblCol") || k.equals("inputCol")) {
                    templateOptions.put(k.trim(), Integer.valueOf(v));
                } else if (k.equals("hide")) {
                    node.put("hideExpression", v);
                } else {
                    config.put(k, setValue(v));
                }
            });

            /**查询和查找表单中，下拉控件增加提示**/
            if (element.getAttributes().containsKey("widget")
                    && "select".equals(element.getAttributes().get("widget"))
                    && !element.getAttributes().containsKey("placeholder")
                    && !element.getAttributes().containsKey("items")
                    && !element.getAttributes().containsKey("autoSearch")) {
                templateOptions.put("placeholder", format("请输入{0}编码或名称", element.getAttributes().get("label")));
            }

            if (!templateOptions.containsKey("divCol")) {
                String col = element.getParent().getAttribute("fieldCol");
                if (!StringUtils.isEmpty(col)) templateOptions.put("divCol", Integer.valueOf(col));
            }
            if (!templateOptions.containsKey("lblCol")) {
                String col = element.getParent().getAttribute("fieldLblCol");
                if (!StringUtils.isEmpty(col)) templateOptions.put("lblCol", Integer.valueOf(col));
            }
            if (!templateOptions.containsKey("inputCol")) {
                String col = element.getParent().getAttribute("fieldInputCol");
                if (!StringUtils.isEmpty(col)) templateOptions.put("inputCol", Integer.valueOf(col));
            }
            Object col = element.getParent().getAttribute("fieldFlex");
            if (!StringUtils.isEmpty(col)) templateOptions.put("fieldFlex", col);
            templateOptions.put("config", config);
            node.put("templateOptions", templateOptions);
            boolean isTable = element.getParent().getName().equals("table");
            /** 处理 depend 元素**/
            List depends = new ArrayList();
            element.getNestedElements().stream().filter(e -> e.getName().equals("depend")).forEach(e -> {
                depends.add(setOnDepends(element.getAttributes().get("name"), isTable, e.parse()));
            });
            if (!depends.isEmpty()) {
                node.put("onDepends", JSONObject.toJSONString(depends));
            }
            /** 处理 lifecycle 元素 */
            element.getNestedElements().stream().filter(e -> e.getName().equals("lifecycle")).forEach(e -> {
                lifecycle.put(e.getAttributes().get("name").trim(), e.parse());
            });

            node.put("hooks", lifecycle);

            /** 处理 event 元素**/
            List events = element.getNestedElements().stream().filter(e -> e.getName().equals("event"))
                    .map(apesElement -> apesElement.parse()).collect(Collectors.toList());
            if (!events.isEmpty()) {
                templateOptions.put("events", events);
            }
        } else {
            Map templateOptions = new HashMap();
            Map expressionProperties = new HashMap();
            Map cellEditorParams = new HashMap();
            Map<String, String> lifecycle = new HashMap();
            boolean isTable = element.getParent().getName().equals("table");
            if (!isTable) {
                if (!element.getAttributes().containsKey("label")) {
                    element.getAttributes().put("label", "");
                }
            } else {
                node.put("cellEditor", "input");
            }
            element.getAttributes().forEach((k, v) -> {
                if (k.equals("name")) {
                    node.put(isTable ? "field" : "key", v.trim());
                } else if (k.equals("widget")) {
                    node.put(isTable ? "cellEditor" : "type", v.trim());
                    if (isTable && "select".equals(v.trim())){
                        node.put("cellRenderer", "renderSelect");
                    }
                } else if (k.equals("attrs")) {
                    Map<String, String> attrs = new AttrsMatcher().parse(v.trim());
                    attrs.forEach((k1, v1) -> {
                        v1 = v1.replace(ExpressionMatcher.getPlaceholder(), isTable ? "ctx." : "formState.");
                        if (k1.equals("hide")) {
                            node.put("hideExpression", v1);
                        } else {
                            expressionProperties.put("templateOptions." + k1, v1);
                        }
                    });
                } else if (k.equals("defaultValue")) {
                    node.put("defaultValue", v.trim());
                } else if (k.equals("validators")) {
                    node.put("validators", v.trim());
                } else if (k.equals("domain") && !isTable) {
                    templateOptions.put(k.trim(), new DomainMatcher().parse(v.trim()));
                } else if (k.equals("hide")) {
                    node.put("hideExpression", isTable ? v : v.replaceAll("ctx.", "formState."));
                } else if (k.equals("disabled") || k.equals("required") || k.equals("readonly")) { // || k.equals("visible")
                    expressionProperties.put("templateOptions." + k, isTable ? v : v.replaceAll("ctx.", "formState."));
                } else if (k.equals("onDepends")) {
                    Object result = setOnDepends(element.getAttributes().get("name"), isTable, Tools.getJO(v));
                    node.put(k.trim(), JSONObject.toJSONString(result));
                } else if (k.equals("divCol") || k.equals("lblCol") || k.equals("inputCol")) {
                    templateOptions.put(k.trim(), Integer.valueOf(v));
                } else if (isTable) {
                    if (k.equals("label")) {
                        node.put("headerName", v);
                    } else if (k.equals("valueGetter")) {
                        node.put("valueGetter", v.indexOf("||") > -1 ? v.trim() : v.trim() + " || ''");
                    } else if (k.equals("width")) {
                        node.put(k.trim(), Integer.valueOf(v));
                    } else if (k.equals("api") || k.equals("value") || k.equals("formatter") || k.equals("values") || k.equals("init") || k.equals("searchFormId")) {
                        cellEditorParams.put(k.trim(), k.equals("values") ? JSON.parseArray(v.trim()) : v.trim());
                    } else if (k.equals("domain")) {
                        cellEditorParams.put(k.trim(), new DomainMatcher().parse(v.trim()));
                    } else {
                        node.put(k.trim(), v);
                    }
                } else {
                    if (LogicalUtil.in(k.trim(), FormConst.lifecycle)) {
                        lifecycle.put(k.trim(), v);
                    } else {
                        templateOptions.put(k.trim(), v);
                    }
                }
            });

            /**取值优先级：field.divCol -> group.fieldCol -> 默认值：8 **/
            if (!templateOptions.containsKey("divCol")) {
                Object col = element.getParent().getAttribute("fieldCol");
                if (col != null) templateOptions.put("divCol", col);
            }
            if (!templateOptions.containsKey("lblCol")) {
                Object col = element.getParent().getAttribute("fieldLblCol");
                if (col != null) templateOptions.put("lblCol", col);
            }
            if (!templateOptions.containsKey("inputCol")) {
                Object col = element.getParent().getAttribute("fieldInputCol");
                if (col != null) templateOptions.put("inputCol", col);
            }
            Object col = element.getParent().getAttribute("fieldFlex");
            if (col != null) templateOptions.put("fieldFlex", col);

            /** 处理 depend 元素**/
            List depends = new ArrayList();
            element.getNestedElements().stream().filter(e -> e.getName().equals("depend")).forEach(e -> {
                depends.add(setOnDepends(element.getAttributes().get("name"), isTable, e.parse()));
            });
            if (!depends.isEmpty()) {
                node.put("onDepends", JSONObject.toJSONString(depends));
            }
            if (isTable && element.getAttributes().containsKey("cellRenderer")) {
                if ("NO".equals(element.getAttributes().get("cellRenderer"))) {
                    node.remove("cellRenderer");
                }
            }
            /** 处理 lifecycle 元素 */
            element.getNestedElements().stream().filter(e -> e.getName().equals("lifecycle")).forEach(e -> {
                lifecycle.put(e.getAttributes().get("name").trim(), e.parse());
            });

            /** 处理 event 元素**/
            List events = element.getNestedElements().stream().filter(e -> e.getName().equals("event"))
                    .map(new Function<ApesElement, Object>() {
                        @Override
                        public Object apply(ApesElement apesElement) {
                            return apesElement.parse();
                        }
                    }).collect(Collectors.toList());
            if (!events.isEmpty()) {
                templateOptions.put("events", events);
            }

            /** 处理 callback 元素**/
            element.getNestedElements().stream().filter(e -> e.getName().equals("callback")).forEach(e -> {
                node.put("callback", e.parse());
            });

            node.put("templateOptions", templateOptions);
            node.put("expressionProperties", expressionProperties);
            if (!cellEditorParams.isEmpty()) {
                node.put("cellEditorParams", cellEditorParams);
            }
            node.put("hooks", lifecycle);
        }
    }

    private void parseNestedElements(ApesElement element, Map node) {
        if (element.getParent().getName().equals("search")) return;
        if (element.getParent().getName().equals("table")) return;
        node.put("fieldGroup", element.getNestedElements().stream()
                .filter(e -> LogicalUtil.notIn(e.getName(), "depend", "event", "lifecycle"))
                .map(apesElement -> apesElement.parse()).collect(Collectors.toList()));
        if (((List)node.get("fieldGroup")).size() == 0){
            node.remove("fieldGroup");
        }
    }

    private Object setOnDepends(String fieldName, boolean isTable, Object data) {
        if (data instanceof List) {
            List result = new JSONArray();
            ((List) data).forEach(o -> {
                result.add(setOnDepends(fieldName, isTable, o));
            });
            return result;
        } else if (data instanceof Map) {
            ((Map) data).put("isTable", isTable);
            ((Map) data).put("field", fieldName);
            return data;
        }
        return null;
    }

    private Object setValue(String value) {
        if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
            return Boolean.valueOf(value.toLowerCase());
        }
        return value;
    }

}
